/*
 * Copyright (C) 2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using DCSharp.Backend.Managers;
using DCSharp.Backend.Objects;
using DCSharp.Hashing;

namespace DCSharp
{
	// TODO: Move the serialization code to Downloader? 
	/// <summary>
	/// Loads and saves the state of downloads to disk.
	/// </summary>
	public class QueueLoader
	{
		private HashStore hashStore;

		/// <summary>
		/// Initializes a new QueueLoader instance.
		/// </summary>
		/// <param name="hashStore">The <see cref="HashStore"/> to load the hash trees from.</param>
		public QueueLoader(HashStore hashStore)
		{
			if (hashStore == null)
			{
				throw new ArgumentNullException("hashStore");
			}
			this.hashStore = hashStore;
		}

		/// <summary>
		/// Loads downloaders from disk.
		/// </summary>
		/// <param name="filename">The file to load from.</param>
		/// <returns>The FileDownloaders loaded.</returns>
		public IEnumerable<FileDownloader> Load(string filename)
		{
			using (XmlTextReader reader = new XmlTextReader(filename))
			{
				return Load(reader);
			}
		}

		/// <summary>
		/// Saves a list of downloaders to disk.
		/// </summary>
		/// <param name="downloaders">The downloaders to save.</param>
		/// <param name="filename">The file to save to.</param>
		public void Save(IEnumerable<Downloader> downloaders,
			string filename)
		{
			// Omit the byte order mark
			Encoding encoding = new UTF8Encoding(false);

			using (XmlTextWriter writer = new XmlTextWriter(filename, encoding))
			{
				writer.Formatting = Formatting.Indented;
				writer.IndentChar = '\t';
				writer.Indentation = 1;

				Save(downloaders, writer);
			}
		}

		protected IEnumerable<FileDownloader> Load(XmlTextReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			if (!reader.ReadToFollowing("Downloads"))
			{
				return null;
			}

			List<FileDownloader> downloaders = new List<FileDownloader>();
			while (reader.ReadToFollowing("Download"))
			{
				try
				{
					RemoteFileInfo remoteFile = new RemoteFileInfo(
						reader.GetAttribute("Name"), long.Parse(reader.GetAttribute("Size")),
						reader.GetAttribute("TTH"));

					string savePath = reader.GetAttribute("Target");

					FileDownloader downloader = new FileDownloader(remoteFile,
						savePath, hashStore);

					XmlReader subtree = reader.ReadSubtree();
					while (subtree.ReadToFollowing("Source"))
					{
						Uid cid = new Uid(reader.GetAttribute("Cid"));
						User user = User.Create(cid, reader.GetAttribute("Nick"));
						//string path = reader.GetAttribute("Path");

						downloader.AddSource(user);
					}
					downloaders.Add(downloader);
				}
				catch
				{
					// Ignore invalid data
				}
			}
			return downloaders;
		}

		protected void Save(IEnumerable<Downloader> downloaders,
			XmlTextWriter writer)
		{
			if (downloaders == null)
			{
				throw new ArgumentNullException("downloaders");
			}
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}

			// Include the standalone attribute
			writer.WriteStartDocument(true);
			writer.WriteStartElement("Downloads");

			foreach (Downloader d in downloaders)
			{
				FileDownloader downloader = d as FileDownloader;
				if (downloader == null)
				{
					continue;
				}

				RemoteFileInfo remoteFile = downloader.RemoteFile;

				writer.WriteStartElement("Download");
				writer.WriteAttributeString("Name", remoteFile.Name);
				writer.WriteAttributeString("Size", remoteFile.Size.ToString());
				writer.WriteAttributeString("TTH", remoteFile.TTH);
				writer.WriteAttributeString("Target", downloader.SavePath);

				foreach (SourceInfo source in downloader)
				{
					User user = source.User;

					writer.WriteStartElement("Source");
					writer.WriteAttributeString("Cid", user.Uid);
					writer.WriteAttributeString("Nick", user.Nick);
					//writer.WriteAttributeString("Path", source.Path);
					writer.WriteEndElement();
				}
				writer.WriteEndElement();
			}
			writer.WriteEndElement();
		}
	}
}
